#include "settings.h"

#include <QtCore/QSettings>
#include <QtCore/QStringList>

using namespace core;

Settings* settings = 0;

Settings* Settings::getInstance()
{
    if (settings == 0)
    {
        settings = new Settings();
    }

    return settings;
}

void Settings::destroy()
{
    delete settings;
    settings = 0;
}

void Settings::addCategory(const QString& category, const QString& text)
{
    if (!m_categories.contains(category))
    {
        m_categories[category] = text;
        m_options[category] = QList<QString>();
    }
}

void Settings::removeCategory(const QString& category)
{
    if (m_categories.contains(category))
    {
        m_categories.remove(category);

        const QList<QString> options = m_options[category];

        foreach (const QString& option, options)
        {
            QPair<QString, QString> combinedKey(category, option);

            m_values.remove(combinedKey);
        }

        m_options.remove(category);
    }
}

QString Settings::categoryText(const QString& category) const
{
    QString ret = "";

    if (m_categories.contains(category))
    {
        ret = m_categories[category];
    }

    return ret;
}

void Settings::setCategoryText(const QString& category, const QString& text)
{
    if (m_categories.contains(category) && m_categories[category] != text)
    {
        m_categories[category] = text;

        emit categoryTextChanged(category, text);
    }
}

QList<QString> Settings::categories() const
{
    return m_categories.keys();
}

void Settings::addOption(const QString& category, const QString& name,
    const QString& text, const QVariant& value)
{
    QPair<QString, QString> combinedKey(category, name);

    if (!m_values.contains(combinedKey))
    {
        m_options[category].append(name);
        m_values[combinedKey] = QPair<QString, QVariant>(text, value);
    }
}

void Settings::removeOption(const QString& category,
    const QString& name)
{
    QPair<QString, QString> combinedKey(category, name);

    if (m_values.contains(combinedKey))
    {
        m_values.remove(combinedKey);
        m_options[category].removeAll(name);

        if (m_options[category].size() == 0)
        {
            m_options.remove(category);
        }
    }
}

QList<QString> Settings::optionsInCategory(
    const QString& category) const
{
    QList<QString> ret;

    if (m_options.contains(category))
    {
        ret = m_options[category];
    }

    return ret;
}

QString Settings::optionText(const QString& category,
    const QString& name) const
{
    QString ret = "";

    QPair<QString, QString> combinedKey(category, name);

    if (m_values.contains(combinedKey))
    {
        ret = m_values[combinedKey].first;
    }

    return ret;
}

QVariant Settings::optionValue(const QString& category,
    const QString& name) const
{
    QVariant ret;

    QPair<QString, QString> combinedKey(category, name);

    if (m_values.contains(combinedKey))
    {
        ret = m_values[combinedKey].second;
    }

    return ret;
}

void Settings::setOptionText(const QString& category,
    const QString& name, const QString& text)
{
    QPair<QString, QString> combinedKey(category, name);

    if (m_values.contains(combinedKey) && m_values[combinedKey].first != text)
    {
        m_values[combinedKey].first = text;

        emit textChanged(category, name, text);
    }
}

void Settings::setOptionValue(const QString& category,
    const QString& name, const QVariant& value)
{
    QPair<QString, QString> combinedKey(category, name);

    if (m_values.contains(combinedKey) && m_values[combinedKey].second.type()
        == value.type())
    {
        m_values[combinedKey].second = value;

        emit optionChanged(category, name, value);
    }
}

void Settings::read(QSettings* settings)
{
    const QStringList groups = settings->childGroups();

    if (groups.contains("settings"))
    {
        settings->beginGroup("settings");

        const QStringList sGroups = settings->childGroups();

        foreach (const QString& category, sGroups)
        {
            settings->beginGroup(category);

            const QStringList options = settings->childKeys();

            foreach (const QString& name, options)
            {
                QPair<QString, QString> combinedKey(category, name);

                Q_ASSERT(m_values.contains(combinedKey));

                const QVariant val = settings->value(name);

                m_values[combinedKey].second =
                    m_values[combinedKey].second.type() == QVariant::Bool ?
                        QVariant(val.toBool()) : val;
            }

            settings->endGroup();
        }

        settings->endGroup();
    }
}

void Settings::write(QSettings* settings)
{
    settings->beginGroup("settings");

    foreach (const QString& category, m_categories)
    {
        settings->beginGroup(category);

        foreach (const QString& name, m_options[category])
        {
            QPair<QString, QString> combinedKey(category, name);

            settings->setValue(name, m_values[combinedKey].second);
        }

        settings->endGroup();
    }

    settings->endGroup();
}

void Settings::addObserver(const QString& property, IObserver* observer)
{
    if ((m_observers.contains(property) &&
        !m_observers[property].contains(observer)))
    {
        m_observers[property].append(observer);
    }
    else if (!m_observers.contains(property))
    {
        m_observers[property] = QList<IObserver*>();
        m_observers[property].append(observer);
    }
}

void Settings::removeObserver(const QString& property, IObserver* observer)
{
    if (m_observers.contains(property) &&
        m_observers[property].contains(observer))
    {
        m_observers[property].removeAll(observer);
    }
}

void Settings::removeObservers(const QString& property)
{
    if (m_observers.contains(property))
    {
        m_observers.remove(property);
    }
}

Settings::Settings() :
    m_categories(),
    m_options(),
    m_values(),
    m_observers()
{
    connect(this, SIGNAL(optionChanged(QString, QString, QVariant)),
        this, SLOT(notifyObservers(QString, QString, QVariant)),
        Qt::QueuedConnection);
}

Settings::~Settings()
{

}


void Settings::notifyObservers(const QString& category,
    const QString& name, const QVariant& value)
{
    if (m_observers.contains(name))
    {
        const QList<IObserver*> observers = m_observers[name];

        foreach (IObserver* observer, observers)
        {
            observer->notify(this, name, value);
        }
    }
}
